from khronos import Atomic, Delay

class Collector(Atomic):
    """Generic component for periodic collection of data.
    Function signatures:
        constructor(): build and return a statistic object.
        getter(): retrieve the value we want to collect.
        collector(proc, stat, value): register the value retrieved with getter() in the statistic 
            object created with constructor(). The proc argument passed to the collector function 
            is the Collector component that makes the call. This is mainly used for obtaining the 
            current simulation time to create timeseries with proc.get_timedelta() (see 
            TSeriesCollector below for an example)."""
    def __init__(self, constructor=None, collector=None, getter=None, period=None, 
                 name=None, parent=None, priority=None):
        Atomic.__init__(self, name, parent, priority)
        self.__stat = None
        self.__constructor = constructor
        self.__collector = collector
        self.__getter = getter
        self.__period = period
        self.__collects = 0
        
    def status(self):
        return "%d collects" % (self.__collects,)
        
    def initializer(self):
        if not callable(self.__period):
            period = self.__period
            self.__period = lambda: period
        self.__stat = self.__constructor()
        self.__collects = 0
        return Delay(self.__period())(activation=self.collect)
        
    def collect(self):
        self.__collector(self, self.__stat, self.__getter())
        self.__collects += 1
        return Delay(self.__period())(activation=self.collect)
    
    @property
    def stat(self):
        return self.__stat
        
    # --------------------------------------------------------------------------
    # FUNCTION DECORATORS (used to configure the collector's behavior)
    def constructor(self, fnc):
        self.__constructor = fnc
        return fnc
        
    def collector(self, fnc):
        self.__collector = fnc
        return fnc
        
    def getter(self, fnc):
        self.__getter = fnc
        return fnc
        
    # --------------------------------------------------------------------------
    
from khronos.support import Call, TimeDelta
from khronos.statistics import FTable, Tally, TSeries
from khronos.statistics.autostat import (ftable_collect, tally_collect, 
                                         tseries_collect_tu, tseries_collect_td)

def FTableCollector(getter=None, period=None, 
                    name=None, parent=None, priority=None):
    constructor = Ftable
    collector = ftable_collect
    return Collector(constructor, collector, getter, period, name, parent, priority)
    
def TallyCollector(getter=None, period=None, 
                   name=None, parent=None, priority=None, 
                   storing=False, numeric=True):
    constructor = Call(Tally, storing=storing, numeric=numeric)
    collector = tally_collect
    return Collector(constructor, collector, getter, period, name, parent, priority)
    
def TSeriesCollector(getter=None, period=None, 
                     name=None, parent=None, priority=None, 
                     storing=False, numeric=True, timescale=0):
    constructor = Call(TSeries, 
                       storing=storing, 
                       numeric=numeric, 
                       timescale=timescale)
    collector = tseries_collect_td if isinstance(timescale, TimeDelta) else tseries_collect_tu
    return Collector(constructor, collector, getter, period, name, parent, priority)
    
